﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.GridFS;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace InfrastructureBase.Data
{
    public class MongoConnection : IMongoConnection
    {
        public readonly string defaultCollectionName;
        public readonly string defaultDataBaseName;
        private readonly MongoClient client;
        private readonly IMongoDatabase database;
        public MongoConnection(MongoClient _client, string _dataBaseName, string _collectionName)
        {
            client = _client;
            defaultDataBaseName = _dataBaseName;
            defaultCollectionName = _collectionName;
            database = GetDataBase();
        }

        public IMongoDatabase Database { get { return database ?? GetDataBase(); } }

        /// <summary>
        /// 获取数据库连接
        /// </summary>
        public MongoClient GetMongoClient { get { return this.client ?? throw new ArgumentException("参数异常，不能为空"); } }

        /// <summary>
        /// 获取数据库
        /// </summary>
        /// <returns></returns>
        public IMongoDatabase GetDataBase(string dataBaseName = default)
        {
            return client.GetDatabase(dataBaseName ?? defaultDataBaseName);
        }

        /// <summary>
        /// 获取集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IMongoCollection<T> GetCollections<T>()
        {
            return this.database.GetCollection<T>(defaultCollectionName ?? nameof(T));
        }

        /// <summary>
        /// 新增文件到集合
        /// </summary>
        public void AddEntityToCollection<T>(T item, string collectionName = default)
        {
            if (item == null)
                throw new ArgumentException("参数异常，不能为空");
            this.database.GetCollection<T>(collectionName ?? defaultCollectionName).InsertOne(item);
        }

        /// <summary>
        /// 新增文件到集合
        /// </summary>
        public async Task AddEntityToCollectionAsync<T>(T item, string collectionName = default)
        {
            if (item == null)
                throw new ArgumentException("参数异常，不能为空");
            await this.database.GetCollection<T>(collectionName ?? defaultCollectionName).InsertOneAsync(item);
        }

        /// <summary>
        /// 新增文件到集合
        /// </summary>
        public void AddEntitiesToCollection<T>(List<T> items, string collectionName = default)
        {
            if (items == null && items.Count == 0)
                throw new ArgumentException("参数异常，不能为空");
            this.database.GetCollection<T>(collectionName ?? defaultCollectionName).InsertMany(items);
        }

        /// <summary>
        /// 删除实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="item"></param>
        /// <param name="collectionName"></param>
        public void DeleteEntityToCollection<T>(FilterDefinition<T> item, string collectionName = default)
        {
            if (item == null)
                throw new ArgumentException("参数异常，不能为空");
            this.database.GetCollection<T>(collectionName ?? defaultCollectionName).DeleteMany(item);
        }

        /// <summary>
        /// 更新数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="filterDefinition"></param>
        /// <param name="item"></param>
        public void UpdateEntityToCollection<T>(FilterDefinition<T> filterDefinition, UpdateDefinition<T> item, string collectionName = default)
        {
            if (item == null || filterDefinition == null)
                throw new ArgumentException("参数异常，不能为空");

            var collection = this.database.GetCollection<T>(collectionName ?? defaultCollectionName);

            if (collection.Find(filterDefinition) == null)
                throw new ArgumentException("参数异常");

            collection.UpdateOne(filterDefinition, item);
        }

        /// <summary>
        /// 获取实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func"></param>
        /// <param name="collectionName"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public T GetEntity<T>(Expression<Func<T, bool>> func, string collectionName = default)
        {
            if (func == null)
                throw new ArgumentException("参数异常，不能为空");
            return this.database.GetCollection<T>(collectionName ?? defaultCollectionName).Find(func).FirstOrDefault();
        }

        /// <summary>
        /// 获取多个
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func"></param>
        /// <param name="collectionName"></param>
        /// <returns></returns>
        public IEnumerable<T> GetEntities<T>(Expression<Func<T, bool>> func = null, string collectionName = null)
        {
            if (func == null)
                throw new ArgumentException("参数异常，不能为空");
            return this.database.GetCollection<T>(collectionName ?? defaultCollectionName).Find(func).ToEnumerable();
        }

        /// <summary>
        /// 获取多个
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="filterDefinition"></param>
        /// <param name="collectionName"></param>
        /// <returns></returns>
        public IEnumerable<T> GetEntitiesV1<T>(FilterDefinition<T> filterDefinition, string collectionName = null)
        {
            if (filterDefinition == null)
                throw new ArgumentException("参数异常，不能为空");
            return this.database.GetCollection<T>(collectionName ?? defaultCollectionName).Find(filterDefinition).ToEnumerable();
        }

        /// <summary>
        /// 获取分页列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="filterDefinition"></param>
        /// <param name="sortDefinition"></param>
        /// <returns></returns>
        public List<T> GetPaingEntitiesData<T>(FilterDefinition<T> filterDefinition, SortDefinition<T> sortDefinition = null, string collectionName = null)
        {
            return this.database.GetCollection<T>(collectionName ?? defaultCollectionName)
                .Find(filterDefinition)
                .Sort(sortDefinition)
                .ToList();
        }

        /// <summary>
        /// 多个文件上传
        /// </summary>
        public async Task<List<ObjectId>> UploadFileToCollectionAsync(IFormFileCollection formFiles)
        {
            if (formFiles == null)
                throw new ArgumentException("参数异常，不能为空");

            List<Task<ObjectId>> tasks = new List<Task<ObjectId>>();
            foreach (IFormFile item in formFiles)
            {
                tasks.Add(Task.Run(() => GetObjectIdByFileAsync(item, ReadFileType.STREAM)));
            }
            var response = await Task.WhenAll(tasks);
            return response.ToList();
        }

        /// <summary>
        /// 文件上传
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public async Task<ObjectId> GetObjectIdByFileAsync(IFormFile item, ReadFileType readFileType = ReadFileType.STREAM)
        {
            ObjectId objectId = default;
            IGridFSBucket bucket = new GridFSBucket(database);
            var options = new GridFSUploadOptions
            {
                ChunkSizeBytes = (int)item.Length,
                Metadata = new BsonDocument
                {
                      { "file-type", item.ContentType},
                      { "file-size", item.Length }
                }
            };


            if (Enum.Parse<ReadFileType>("0") == readFileType)
            {
                Stream stream = item.OpenReadStream();
                objectId = await bucket.UploadFromStreamAsync(item.FileName, stream, options);
            }
            else
            {
                using (var Stream = item.OpenReadStream())
                {
                    byte[] contents = new byte[Stream.Length];
                    Stream.Read(contents, 0, contents.Length);
                    Stream.Seek(0, SeekOrigin.Begin); // 设置当前流的位置为流的开始
                    objectId = await bucket.UploadFromBytesAsync(item.FileName, contents, options);
                }
            }
            return objectId;
        }

        /// <summary>
        /// objectId获取文件
        /// </summary>
        /// <param name="objectId"></param>
        /// <returns></returns>
        public async Task<ObjectId> GetFileByObjectIdAsync(ObjectId objectId)
        {
           
            IGridFSBucket bucket = new GridFSBucket(database);
            using (GridFSDownloadStream<ObjectId> gridFSDownloadStream = await bucket.OpenDownloadStreamAsync(objectId))
            {
                string path = Path.Combine(Directory.GetCurrentDirectory(), "DownloadAttachment");
               if ( !Directory.Exists(path))
                    Directory.CreateDirectory("DownloadAttachment");

                string tagrgetPath = Path.Combine(path, gridFSDownloadStream.FileInfo.Filename);
                using (FileStream fsWrite = new FileStream(tagrgetPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
                {
                    //开辟临时缓存内存
                    byte[] buffer = new byte[1024 * 1024];
                    while (true)
                    {
                        //readCount是真正读取到的字节数
                        int readCount = gridFSDownloadStream.Read(buffer, 0, buffer.Length);
                        //写入目标文件
                        fsWrite.Write(buffer, 0, readCount);
                        //判断是否读取完成
                        if (readCount < buffer.Length)
                        {
                            break;
                        }
                    }
                }
            }
            return objectId;
        }                                                                            

        public enum ReadFileType : int
        {
            STREAM = 0,
            BYTE = 1
        }
    }

}

